
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>

#define BLOCK_SIZE 512
#define EMPTY_BLOCK "EMPTY"
#define DIRECTORY 0
#define FILE 1

struct superblock {
	unsigned short isize;
	unsigned short fsize;
	unsigned short nfree;
	unsigned short free[100];
	unsigned short ninode;
	unsigned short inode[100];
	char flock;
	char ilock;
	char fmod;
	unsigned short time[2];
};

struct inode {
	unsigned short flags;
	char nlinks;
	char uid;
	char gid;
	char size0;
	unsigned short size1;
	unsigned short addr[8];
	unsigned short actime[2];
	unsigned short modtime[2];
};

struct directoryentry {
	unsigned short inodenumber;
	char filename[14];
};

unsigned long numberofinodesrem;
unsigned long numofdatablocksrem;
char currentpath[200]; /*This gives you the current working directory*/
unsigned short cparentinode; /*Current Parent inode #*/
unsigned short cinode; /*Current inode #*/
unsigned short iptr;
struct superblock superblock;
int fsfd;

int EmptyTheBlocks(unsigned long totalfsblocks) {
	int i, byteswritten;
	char emptycontent[BLOCK_SIZE];

	for (i = 0; i < BLOCK_SIZE; i++) {
		emptycontent[i] = 0;
	}

	for (i = 0; i < totalfsblocks; i++) {
		byteswritten = write(fsfd, emptycontent, BLOCK_SIZE);
		if (byteswritten != BLOCK_SIZE)
			printf("ERROR: Unable to empty the FS blocks\n");
	}

	return 1;
}

int InitializeSuperBlock(unsigned long inodeblocks) {
	int i, rvalue;
	time_t presenttime;
	struct tm *currenttime;
	presenttime = time(0); /*This gives current time in secs*/
	currenttime = localtime(&presenttime); /*This converts secs into hrs and mins and returns a pointer to a 'struct tm'*/

	memset(&superblock, 0, sizeof(struct superblock));
	superblock.isize = inodeblocks;
	superblock.fsize = 0;
	superblock.nfree = 100;
	if (numofdatablocksrem >= 100) {
		for (i = 0; i < 100; i++) {
			superblock.free[i] = i; /*The first block is for the root directory. Block address is (2+inodeblocks)*BLOCK_SIZE*/
		}
	} else {
		for (i = 0; i < numofdatablocksrem; i++) {
			superblock.free[i] = i;
		}
		superblock.nfree = numofdatablocksrem;
	}
	superblock.ninode = 100;

	if ((inodeblocks * 16) > 100) {
		for (i = 0; i < 100; i++) {
			superblock.inode[i] = i; /*First inode (superblock.inode[0]) is for the root directory.*/
		}
	} else {
		for (i = 0; i < (inodeblocks * 16); i++) {
			superblock.inode[i] = i;
		}
		superblock.ninode = inodeblocks * 16;
	}
	superblock.flock = 0;
	superblock.ilock = 0;
	superblock.fmod = 0;
	superblock.time[0] = currenttime->tm_hour;
	superblock.time[1] = currenttime->tm_min;
	/*Writing to Superblock*/
	rvalue = lseek(fsfd, BLOCK_SIZE, SEEK_SET);
	if ((write(fsfd, &superblock, sizeof(struct superblock)))
			!= sizeof(struct superblock))
		printf("ERROR:Superblock not written\n");

	return 1;
}

int CreateRootDirectory(struct inode inode, unsigned long inodeblocks) {
	int rvalue;
	int i;
	unsigned long position;
	char buffer[14];
	struct directoryentry rdirectoryentry;
	time_t presenttime;
	struct tm *currenttime;
	presenttime = time(0); /*This gives current time in secs*/
	currenttime = localtime(&presenttime); /*This converts secs into hrs and mins and returns a pointer to a 'struct tm'*/

	memset(&inode, 0, sizeof(struct inode));
	inode.flags = 49572;
	inode.nlinks = 0;
	inode.uid = 1;
	inode.gid = 1;
	inode.size0 = 0; /*size0 and size1 gives the size of the file itself.*/
	inode.size1 = BLOCK_SIZE;
	/*Each directory has only 1 block of memory (I mean 512 Bytes). Therfore only 32 entries in 1 directory.*/

	inode.addr[0] = (2 + inodeblocks) * BLOCK_SIZE; /*First block after i-list is for the superblock.*/

	inode.actime[0] = currenttime->tm_hour;
	inode.actime[1] = currenttime->tm_min;
	inode.modtime[0] = currenttime->tm_hour;
	inode.modtime[1] = currenttime->tm_min;

	/*As you are allocating 1 inode to the root directory. This is the first inode in the i-list blocks.*/
	superblock.ninode--; /*The number free inodes IN THE SUPERBLOCK is reduced by 1*/
	numberofinodesrem--; /*The number of inodes now is (inodeblocks*16)-1*/
	numofdatablocksrem--; /*Total # of data blocks remaining now.*/

	/*Superblock modified; Hence update*/
	rvalue = lseek(fsfd, BLOCK_SIZE, SEEK_SET);
	if ((write(fsfd, &superblock, sizeof(struct superblock)))
			!= sizeof(struct superblock)) {
		printf("ERROR:Superblock not written\n");
		exit(1);
	}

	/*Write Inode info of the Root Directory.*/
	rvalue = lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * superblock.inode[iptr])),
			SEEK_SET); /*This location contains the info abt the root directory*/
	iptr++; /*Points to the next free inode in the superblock. As it is
	 inc by 1 the next free inode would be
	 superblock.inode[iptr]=(2*BLOCK_SIZE)+(32*iptr)*/

	if ((write(fsfd, &inode, sizeof(struct inode))) != sizeof(struct inode)) {
		printf("ERROR:Root inode not written to the 1st block\n");
		exit(1);
	}

	rvalue = lseek(fsfd, inode.addr[0], SEEK_SET); /*Move to the first block right after the 'Inode List'*/
	rdirectoryentry.inodenumber = 0;

	/*Now create directory entries '.'-current directory and '..'-parent directory*/

	sprintf(buffer, ".");
	strcpy(rdirectoryentry.filename, buffer);
	if ((write(fsfd, &rdirectoryentry.inodenumber, sizeof(unsigned short)))
			!= sizeof(unsigned short)) {
		printf("ERROR:In Root Dir root inode number not written.\n");
		exit(1);
	}
	if ((write(fsfd, rdirectoryentry.filename, sizeof(rdirectoryentry.filename)))
			!= sizeof(rdirectoryentry.filename)) {
		printf("ERROR:Root filename not written.\n");
		exit(1);
	}
	sprintf(buffer, "..");
	strcpy(rdirectoryentry.filename, buffer);
	if ((write(fsfd, &rdirectoryentry.inodenumber, sizeof(unsigned short)))
			!= sizeof(unsigned short)) {
		printf("ERROR:In Root Dir parent inode number not written.\n");
		exit(1);
	}
	if ((write(fsfd, rdirectoryentry.filename, sizeof(rdirectoryentry.filename)))
			!= sizeof(rdirectoryentry.filename)) {
		printf("ERROR:Parent filename in Root Dir not written.\n");
		exit(1);
	}

position=inode.addr[0]+32;

for(i=0;i<30;i++)
{
	rvalue = lseek(fsfd, 2, SEEK_CUR);
	write(fsfd, EMPTY_BLOCK, 14);
}

	strcpy(currentpath, "/"); /*Root Directory.*/

	close(fsfd);
	return 1;
}


int FileSystemInitialization(unsigned long totalfsblocks,
		unsigned long inodeblocks) {
	numofdatablocksrem = totalfsblocks - inodeblocks - 2; /*Blocks to store the contents of the file. Block 0 for Boot Strap and Block 1 for Superblock*/
	numberofinodesrem = inodeblocks * 16;
	struct inode inode;

	if (totalfsblocks < inodeblocks) {
		printf(
				"Arguements invalid; Total blocks cannot be less than inode blocks\n");
		exit(1);
	}
	/*To open the FS file*/
	if ((fsfd = open("v6fs", O_CREAT | O_RDWR, 0664)) < 0) {
		printf("ERROR in init: fsfd<0 fsfd value=%d\n", fsfd);
		exit(1);
	}

	/*Empty all the blocks of the File System*/
	EmptyTheBlocks(totalfsblocks);

	/*Create and Write to Super Block*/
	InitializeSuperBlock(inodeblocks);

	/*Create root directory*/
	CreateRootDirectory(inode, inodeblocks);
	return 1;
}

////////////////////////////////////////////////////////
//////////// Create a new Directory ///////////////////
///////////////////////////////////////////////////////

////////////////////// helper functions ///////////////

//set the initial values for inode
struct inode initialize_inode(unsigned long inodeblocks, int identifier, int blocksreqd, unsigned long unxfilesize)
{
	int i;
	struct inode inode;
	memset(&inode, 0, sizeof(struct inode));
	time_t presenttime;
	struct tm *currenttime;
	presenttime = time(0); /*This gives current time in secs*/
	currenttime = localtime(&presenttime); /*This converts secs into hrs and mins and returns a pointer to a 'struct tm'*/
	if (identifier==DIRECTORY)
	{
		inode.flags = 49572;
	}
	else if (identifier==FILE)
	{
		inode.flags = 33188;
	}
	else
	{
		printf("Initialize Inode 1: Identifier invalid\n");
		return inode;
	}
	inode.nlinks = 0;
	inode.uid = 1;
	inode.gid = 1;
	inode.size0 = 0; /*size0 and size1 gives the size of the file itself.*/
	if (identifier==DIRECTORY)
		inode.size1 = unxfilesize;
	else if(identifier==FILE)
		inode.size1=unxfilesize;
	else
		{
			printf("Initialize Inode 2: Identifier invalid-assigning # of blocks reqd for a file\n");
			return inode;
		}
	/*Each directory has only 1 block of memory (I mean 512 Bytes). Therfore only 32 entries in 1 directory.*/

	/*Check to see if there are any free data blocks left in the superblock.*/
	superblock.nfree--;
	if (superblock.free[superblock.nfree] == 0)
		{
			printf("ERROR:No more data blocks left\n");
			exit(1);
		}
	if (identifier==DIRECTORY)
		inode.addr[0] = (2 + inodeblocks + superblock.free[superblock.nfree])* BLOCK_SIZE;
	else
		{
			for(i=0;i<blocksreqd;i++)
			{
				inode.addr[i] = (2 + inodeblocks + superblock.free[superblock.nfree])* BLOCK_SIZE;
				superblock.nfree--;
				if (superblock.free[superblock.nfree] == 0)
					{
						printf("ERROR:No more data blocks left\n");
						exit(1);
					}
			}
		}
	inode.actime[0] = currenttime->tm_hour;
	inode.actime[1] = currenttime->tm_min;
	inode.modtime[0] = currenttime->tm_hour;
	inode.modtime[1] = currenttime->tm_min;
	return inode;
}

//update the filesystem inode and superblock
void update_sb_inode(int fsfd, struct inode inode)
{
	lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * superblock.inode[iptr])),
				SEEK_SET); /*superblock.inode[iptr] contains the address of the next free inode*/

	if ((write(fsfd, &inode, sizeof(struct inode))) != sizeof(struct inode)) {
		printf("ERROR in sb_inode:inode info not written\n");
		exit(1);
	}
	superblock.ninode--; /*The number free inodes IN THE SUPERBLOCK is reduced by 1*/
	numberofinodesrem--; /*The number of inodes now is (inodeblocks*16)-1*/
	numofdatablocksrem--; /*Total # of data blocks remaining now.*/

	/*Superblock modified. Write to Superblock.*/
	lseek(fsfd, BLOCK_SIZE, SEEK_SET);

	if ((write(fsfd, &superblock, sizeof(struct superblock)))
			!= sizeof(struct superblock)) {
		printf("ERROR in sb_inode:Superblock not written\n");
		exit(1);
	}

}

//create a empty directory
void initialize_directory(int fsfd, struct inode inode){
	struct directoryentry rdirectoryentry;
	char buffer[14];
	int i;
	/*Create directory entries '.' and '..'*/
		lseek(fsfd, inode.addr[0], SEEK_SET);

		sprintf(buffer, ".");
		strcpy(rdirectoryentry.filename, buffer);
		rdirectoryentry.inodenumber = iptr;

		if ((write(fsfd, &rdirectoryentry.inodenumber, sizeof(unsigned short)))
				!= sizeof(unsigned short)) {
			printf("ERROR in mkdir:inode number not written.\n");
			exit(1);
		}
		if ((write(fsfd, rdirectoryentry.filename, sizeof(rdirectoryentry.filename)))
				!= sizeof(rdirectoryentry.filename)) {
			printf("ERROR in mkdir:filename not written.\n");
			exit(1);
		}

		sprintf(buffer, "..");
		strcpy(rdirectoryentry.filename, buffer);
		rdirectoryentry.inodenumber = cinode;//TODO Check

		if ((write(fsfd, &rdirectoryentry.inodenumber, sizeof(unsigned short)))
				!= sizeof(unsigned short)) {
			printf("ERROR in mkdir:inode number not written.\n");
			exit(1);
		}
		if ((write(fsfd, rdirectoryentry.filename, sizeof(rdirectoryentry.filename)))
				!= sizeof(rdirectoryentry.filename)) {
			printf("ERROR in mkdir:filename not written.\n");
			exit(1);
		}
		lseek(fsfd, 2, SEEK_CUR);
		for (i = 0; i < 30; ++i) {
			write(fsfd, EMPTY_BLOCK, 14);
			lseek(fsfd, 2, SEEK_CUR);
		}

}

//update the parent directory
void update_parent_dir(int fsfd, char* directoryname){
	struct directoryentry rdirectoryentry;
	int i;
	unsigned short position;

	char empty[14];
	lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * cinode) + 8), SEEK_SET);//TODO check again
	/*You've reached the parent inode of this directory you created.'+8' actually moves to the address
	 part of the inode which contains the address of the data block of the parent directory.*/

	read(fsfd, &position, sizeof(unsigned short));
	/*position gives the address of the data block.*/

	position = position + 32; /*Move by extra 32 bytes to skip the parent and the current dir info.*/
	lseek(fsfd, position + 2, SEEK_SET);

	for (i = 0; i < 30; i++) /*i<30 coz you've already moved by 32 bytes.*/
	{
		read(fsfd, empty, 14);
		if (!strcmp(EMPTY_BLOCK,empty) || !strcmp("",empty))
			break;
		position = position + 16;
		lseek(fsfd, position, SEEK_SET);
	}

	lseek(fsfd, position, SEEK_SET); /*Goto that position where you need to make an entry.*/
	rdirectoryentry.inodenumber = iptr;
	//rdirectoryentry.filename=directoryname;

	if ((write(fsfd, &rdirectoryentry.inodenumber, sizeof(unsigned short)))
			!= sizeof(unsigned short)) {
		printf(
				"ERROR in update_parent_dir:inode number of the directory created not written.\n");
		exit(1);
	}
	if ((write(fsfd, directoryname, sizeof(rdirectoryentry.filename)))
			!= sizeof(rdirectoryentry.filename)) {
		printf(
				"ERROR in update_parent_dir:filename of the directory created not written.\n");
		exit(1);
	}
}
/////////////////////////////////////////////////////////////////

////////////////////// Mkdir Command ///////////////////////////

int Mkdir(unsigned long inodeblocks, char *directoryname){
	struct inode inode;
	int identifier=0, blocksreqd=1;
	unsigned short position;
	int i, rvalue;
	char filename[14];

	/*To open the FS file*/
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in mkdir: fsfd<0\n");
		exit(1);
	}

	/*To check if the directory already exists*/

	rvalue=lseek(fsfd, ((BLOCK_SIZE*2)+(32*cinode)+8), SEEK_SET);
	read(fsfd, &position, sizeof(unsigned short));
	/*position gives the address of the data block.*/

	position=position+34;             				/*Move by extra 34 bytes to skip the parent, current dir info and the inode # of the first file.*/
	rvalue=lseek(fsfd, position, SEEK_SET);

	for(i=0;i<30;i++)                         /*i<30 coz you've already moved by 32 bytes.*/
	{
		read(fsfd, filename, 14);                 /*fsfd moves by 14 bytes.*/
		if(!strcmp(directoryname,filename))
			{
				printf("Mkdir : directory already exists.\n");
				return 0;                      					/*Error.*/
			}
		rvalue=lseek(fsfd, 2, SEEK_CUR);
	}

	/*Check if the inodes are available.*/
	if ((iptr == 100) && (superblock.ninode == 0)) /*If iptr goes to 100 fill your inode array in the SB.*/
	{
		printf("ERROR:The number of inodes in the superblock ");
		exit(1);
	}
	inode = initialize_inode(inodeblocks, identifier, blocksreqd, BLOCK_SIZE);
	/*Write the necessary parameters to the inode.*/
	update_sb_inode(fsfd, inode);

	initialize_directory(fsfd, inode);

	/*So, now you've created a new directory. Make an entry for this directory in it's parent directory.*/
	update_parent_dir(fsfd, directoryname);

	iptr++; /*Next available inode number is iptr.*/
	close(fsfd);

	return 1;
}

///////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////
////////////	CHANGE DIRECTORY COMMAND  /////////////
/////////////////////////////////////////////////////


//////////////// Helper functions ///////////////////

//used when user cd's to ..
void truncate_current_path() {
	int i;
	char temp[200];
	int parentroot = 0; //flag to indicate parent is root

	for (i = (sizeof currentpath) - 1; i > 0; i--) {
		if (currentpath[i] == '/') {
			strncpy(temp, currentpath, i);
			temp[i] = '\0';
			strcpy(currentpath, temp);
			break;
		}
		if (i == 1) {
			parentroot = 1;
		}
	}
	if (parentroot) {
		strcpy(currentpath, "/");
	}
}

//returns address of datablock of inode
//NOTE : Might get complicated when files are involved
unsigned short read_dblock_addr_dir(int fsfd, unsigned short inode) {
	unsigned short dblock;
	lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * inode) + 8), SEEK_SET);
	read(fsfd, &dblock, 2);
	return dblock;
}

int get_no_of_dblocks(int fsfd, unsigned short inode){
	lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * inode) + 6), SEEK_SET);

	unsigned short fsize;
	read(fsfd, &fsize, 2);
	if(fsize%BLOCK_SIZE == 0){
		return fsize/BLOCK_SIZE;
	}
	return (fsize/BLOCK_SIZE) + 1;
}

unsigned short* read_dblock_addr_file(int fsfd, unsigned short inode, int no_of_blocks){
	int i;

	unsigned short *dblocks = (unsigned short*) malloc(no_of_blocks*2);

	for (i = 0; i < no_of_blocks; i++) {
		read(fsfd, &dblocks[i], 2);
	}

	return dblocks;
}

//ASSUMPTION : fsfd is at the head of datablock
//returns the parent inode value
unsigned short get_parent_inode(int fsfd) {
	unsigned short p_inode;
	lseek(fsfd, 16, SEEK_CUR);
	read(fsfd, &p_inode, 2);
	return p_inode;
}

//searches for an entry in a directory
//and returns its number in the list
int get_entry_num(int fsfd, char *fname) {
	int i;
	lseek(fsfd, 34, SEEK_CUR);
	char entry[14];
	for (i = 2; i < 32; i++) {
		read(fsfd, entry, 14);
		if (!strcmp(fname, entry)) {
			return i;
		}
		lseek(fsfd, 2, SEEK_CUR);
	}
	return 32;
}


//just for debug
void display_dir_contents(unsigned short in_no){
	int i;
	unsigned short in;
	char name[14];
	/*To open the FS file*/
		if ((fsfd = open("v6fs", O_RDWR)) < 0) {
			printf("ERROR in cd: fsfd<0\n");
			exit(1);
		}

	unsigned short db = read_dblock_addr_dir(fsfd, in_no);
	lseek(fsfd, db, SEEK_SET);

		for (i = 0; i < 32; i++) {
			read(fsfd, &in,2);
			read(fsfd, name, 14);

			printf("%hu\t%s\n", in,name);
		}
		close(fsfd);
}
/////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////// Cd command //////////////////////////////

int Cd_rel(int fsfd, char *fname) {

	//Cd to the same directory
	if (!strcmp(fname, ".")) {
		return 1;
	}

	//Cd to the parent
	if (!strcmp(fname, "..")) {
		truncate_current_path();
		unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
		lseek(fsfd, dblock, SEEK_SET);

		cinode = get_parent_inode(fsfd);
		dblock = read_dblock_addr_dir(fsfd, cinode);
		lseek(fsfd, dblock, SEEK_SET);

		cparentinode = get_parent_inode(fsfd);

		return 1;
	}

	unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
	lseek(fsfd, dblock, SEEK_SET);
	int entry_num = get_entry_num(fsfd, fname);

	if (entry_num < 32) {
		lseek(fsfd, (dblock + (entry_num * 16)), SEEK_SET);
		cparentinode = cinode;
		read(fsfd, &cinode, 2);

		lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * cinode)), SEEK_SET);
		unsigned short flag;
		read(fsfd, &flag, 2);
		if((flag & 24576) == 0){
			printf("Cd: %s: Not a directory\n", fname);
			return 0;
		}

		/*Now, update your current working directory.*/
		if (strcmp(currentpath, "/")) {
			strcat(currentpath, "/");
		}
		strcat(currentpath, fname);

	} else {
		printf("Cd: %s: No such file or directory\n", fname);
		return 0;
	}

	//display_dir_contents(cinode);

	return 1;
}

int Cd(char* fname){

	char *ptr;
	char cp_bckup[200];
	unsigned short ci_bckup;
	unsigned short cpi_bckup;

	/*To open the FS file*/
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in cd: fsfd<0\n");
		exit(1);
	}

	//take a backup of these values
	strcpy(cp_bckup, currentpath);
	ci_bckup = cinode;
	cpi_bckup = cparentinode;

	//is it absolute path?
	if(fname[0] == '/'){
		//start from root node
		cinode = 0;
		cparentinode = 0;
		strcpy(currentpath, "/");
		//if it is just "Cd /" do nothing
		if(strlen(fname) == 1){
			return 1;
		}
	}
		ptr = strtok(fname,"/");

		while(ptr != NULL){
			//if the path string is correct keep doing relative cd
			if(Cd_rel(fsfd, ptr)){
				ptr = strtok(NULL,"/");
			}else{ //if not show error and get back to old values
				strcpy(currentpath, cp_bckup);
				cinode = ci_bckup;
				cparentinode = cpi_bckup;
				return 1;
			}
		}

	close(fsfd);
	return 1;
}


///////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
///////////// Remove Directory Command /////////////////
////////////////////////////////////////////////////////

/////////////// Helper Functions ///////////////////////

//updates the superblock
void update_sb_rm(int fsfd, unsigned short inode, unsigned short *dblock, int no_of_blocks, unsigned long no_of_iblocks) {
	int i;
	unsigned short l_free[100];
	unsigned short l_nfree;
	unsigned short l_inode[100];
	unsigned short l_ninode;

	lseek(fsfd, BLOCK_SIZE + 4, SEEK_SET);
	read(fsfd, &l_nfree, 2);
	read(fsfd, l_free, 200);

	for(i=0 ; i<no_of_blocks; i++){
		l_free[l_nfree] = (dblock[i]/512) - 2 - no_of_iblocks;
		l_nfree++;
	}

	lseek(fsfd, BLOCK_SIZE + 4, SEEK_SET);
	write(fsfd, &l_nfree, 2);
	write(fsfd, l_free, 200);

	read(fsfd, &l_ninode, 2);
	read(fsfd, l_inode, 200);

	l_ninode++;

	lseek(fsfd, BLOCK_SIZE + 206, SEEK_SET);
	write(fsfd, &l_ninode, 2);
	write(fsfd, l_inode, 200);

}

void update_inode_rm(int fsfd, unsigned short inode) {

	unsigned short reset = 0;

	lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * inode)), SEEK_SET);
	write(fsfd, &reset, 2);
}

int isEmpty(int fsfd){
	int i;
	char fname[14];
	lseek(fsfd,34,SEEK_CUR);

	for (i = 2; i < 32; i++) {
		read(fsfd, fname, 14);
		if(strcmp(EMPTY_BLOCK, fname)){
			return 0;
		}
		/*if(strcmp("",fname)){
			return 0;
		}*/

		lseek(fsfd, 2, SEEK_CUR);
	}

	return 1;
}

//////////////////////////////////////////////////////////////////////////

//////////////////////////////// Rmdir Command ///////////////////////////
int Rmdir(char* dirname, unsigned long no_of_iblocks) {

	unsigned short del_inode;
	unsigned short del_dblock;

	//open file system
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in cd: fsfd<0\n");
		exit(1);
	}

	//reach the datablock
	unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
	lseek(fsfd, dblock, SEEK_SET);

	//search for the entry
	int entry_num = get_entry_num(fsfd, dirname);
	if (entry_num < 32) {
		lseek(fsfd, (dblock + (entry_num * 16)), SEEK_SET);
		read(fsfd, &del_inode, 2);


		lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * del_inode)), SEEK_SET);
		unsigned short flag;
		read(fsfd, &flag, 2);
		if ((flag & 24576) == 0) {
			printf("Rmdir: %s: Not a directory\n", dirname);
			close(fsfd);
			return 1;
		}


		del_dblock = read_dblock_addr_dir(fsfd, del_inode);
		lseek(fsfd, del_dblock, SEEK_SET);


		if(!isEmpty(fsfd)){
			printf("Rmdir : %s : Directory not empty\n",dirname);
			close(fsfd);
			return 1;
		}else{
			lseek(fsfd, (dblock + (entry_num * 16) + 2),SEEK_SET);
			write(fsfd, EMPTY_BLOCK, 14);

			del_dblock = read_dblock_addr_dir(fsfd, del_inode);
			update_inode_rm(fsfd, del_inode);
			update_sb_rm(fsfd, del_inode, &del_dblock,1, no_of_iblocks);

			close(fsfd);
			return 1;
		}
	}

	printf("Rmdir: failed to remove '%s': No such file or directory\n", dirname);
	close(fsfd);
	return 1;
}

///////////////////////////////////////////////////////////////////////////////////////////
int Rmfile(char *fname, unsigned long no_of_inodeblocks) {
	unsigned short del_inode;
	unsigned short *dblocks;
	int no_of_blocks;
	//open file system
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in cd: fsfd<0\n");
		exit(1);
	}

	//reach the datablock
	unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
	lseek(fsfd, dblock, SEEK_SET);

	//search for the entry
	int entry_num = get_entry_num(fsfd, fname);
	if (entry_num < 32) {
		lseek(fsfd, (dblock + (entry_num * 16)), SEEK_SET);
		read(fsfd, &del_inode, 2);

		lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * del_inode)), SEEK_SET);
		unsigned short flag;
		read(fsfd, &flag, 2);
		if (!((flag & 24576) == 0)) {
			printf("Rmdir: %s: is a directory\n", fname);
			close(fsfd);
			return 1;
		}

		lseek(fsfd, (dblock + (entry_num * 16) + 2), SEEK_SET);
		write(fsfd, EMPTY_BLOCK, 14);
		no_of_blocks = get_no_of_dblocks(fsfd,del_inode);
		dblocks = read_dblock_addr_file(fsfd, del_inode, no_of_blocks);
		update_sb_rm(fsfd, del_inode, dblocks, no_of_blocks, no_of_inodeblocks);
		update_inode_rm(fsfd, del_inode);

	}
	else{
		printf("Rm: failed to remove '%s': No such file or directory\n", fname);
	}
	close(fsfd);
	return 1;
}

/////////////////////////////////////////////////////////////////////////////
//////////////////////// Chmod Command /////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

//////////////////////// helper functions //////////////////////////////////


//////////////////////// Chmod Command  /////////////////////////////////

int Chmod(unsigned short mode, char *fname) {

	unsigned short mod_inode;
	unsigned short prev_flag;
	unsigned short updated_flag;

	//open file system
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in cd: fsfd<0\n");
		exit(1);
	}

	//reach the datablock
	unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
	lseek(fsfd, dblock, SEEK_SET);

	//search for the entry
	int entry_num = get_entry_num(fsfd, fname);

	if (entry_num < 32) {
		lseek(fsfd, (dblock + (entry_num * 16)), SEEK_SET);
		read(fsfd, &mod_inode, 2);

		lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * mod_inode)), SEEK_SET);
		read(fsfd, &prev_flag, 2);
		updated_flag = prev_flag | mode;

		lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * mod_inode)), SEEK_SET);
		write(fsfd, &updated_flag, 2);

		close(fsfd);
		return 1;
	}

	printf("Chmod: cannot access '%s': No such file or directory\n", fname);
	close(fsfd);
	return 1;

}

///////////////////////////////////////////////////////////////////////////
/////////////////// Cpout ////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////



int Cpout(char* unixfile, char* v6filename) {
	unsigned short cp_inode;
	int no_of_dblocks;
	char data[512];
	unsigned short* dblocks;
	int unxfd;
	int i;

//	mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
	if ((unxfd=open(unixfile, O_CREAT|O_RDWR, 0664))<0)
	{
		printf("Cpout: Error-file '%s' does not exist.\n", unixfile);//TODO
		return 0;
	}

	//open file system
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in cd: fsfd<0\n");
		exit(1);
	}
	//reach the datablock
	unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
	lseek(fsfd, dblock, SEEK_SET);

	//search for the entry
	int entry_num = get_entry_num(fsfd, v6filename);

	if (entry_num < 32) {
		lseek(fsfd, (dblock + (entry_num * 16)), SEEK_SET);
		read(fsfd, &cp_inode, 2);

		lseek(fsfd, ((BLOCK_SIZE * 2) + (32 * cp_inode) + 6), SEEK_SET);
		unsigned short fsize;
		read(fsfd, &fsize, 2);

		no_of_dblocks = get_no_of_dblocks(fsfd, cp_inode);
		dblocks = read_dblock_addr_file(fsfd, cp_inode, no_of_dblocks);

		int final_offset = 512 - ((512*no_of_dblocks) - fsize);

		for (i = 0; i < no_of_dblocks; i++) {
			lseek(fsfd, dblocks[i] , SEEK_SET);
			if (i == (no_of_dblocks - 1)) {
				read(fsfd, data, final_offset);
				write(unxfd, data, final_offset);
			}
		}
	}
	else{
		printf("Cpout : %s : No such file or directory\n", v6filename);
	}
	close(unxfd);
	close(fsfd);
	return 1;
}

int Cpin(unsigned long inodeblocks, char *unixfile, char *v6filename)
{
struct inode inode;
char filename[14];
int unxfd, blocksreqd;
unsigned long bytesread;
unsigned short position;
int identifier=1;       //To indicate that it is a file.
unsigned long unxfilesize;
char buffer[BLOCK_SIZE+1];
int i=0, rvalue;

if ((fsfd = open("v6fs", O_RDWR)) < 0)
{
		printf("ERROR in Cpin: fsfd<0\n");
		exit(1);
}

if ((unxfd=open(unixfile, O_RDONLY))<0)
{
	printf("Cpin: Error-file '%s' does not exist.\n", unixfile);
	return 0;
}

/*To check if the file already exists*/

	rvalue=lseek(fsfd, ((BLOCK_SIZE*2)+(32*cinode)+8), SEEK_SET);
	read(fsfd, &position, sizeof(unsigned short));
	/*position gives the address of the data block.*/

	position=position+34;             				/*Move by extra 34 bytes to skip the parent, current dir info and the inode # of the first file.*/
	rvalue=lseek(fsfd, position, SEEK_SET);

	for(i=0;i<30;i++)                         /*i<30 coz you've already moved by 32 bytes.*/
	{
		read(fsfd, filename, 14);                 /*fsfd moves by 14 bytes.*/
		if(!strcmp(v6filename, filename))
			{
				printf("Cpin : file already exists.\n");
				return 0;                      					/*Error.*/
			}
		rvalue=lseek(fsfd, 2, SEEK_CUR);
	}

memset(buffer, 0, sizeof(buffer));

unxfilesize=lseek(unxfd, 0, SEEK_END);

if ((blocksreqd=unxfilesize%BLOCK_SIZE)==0)
	{
		blocksreqd=unxfilesize/BLOCK_SIZE;
	}
else if((blocksreqd=unxfilesize%BLOCK_SIZE)==unxfilesize)
	{
		blocksreqd=1;
	}
else
	{
		blocksreqd=unxfilesize/BLOCK_SIZE;
		blocksreqd=blocksreqd+1;
	}

inode=initialize_inode(inodeblocks, identifier, blocksreqd, unxfilesize);
update_sb_inode(fsfd, inode);
lseek(unxfd, 0, SEEK_SET);
for(i=0;i<blocksreqd;i++)
	{
		if(blocksreqd==1)
		{
			bytesread=read(unxfd, buffer, unxfilesize);
		}

		if(blocksreqd > 1)
		{
			bytesread=read(unxfd, buffer, BLOCK_SIZE);
		}

		lseek(fsfd, inode.addr[i], SEEK_SET);
		write(fsfd, buffer, BLOCK_SIZE);
	}
/*Now, make an entry for this file in the parent directory.*/
update_parent_dir(fsfd, v6filename);

iptr++;
close(fsfd);
close(unxfd);
return 1;
}

//Only for debugging
void check_flag_values(char *fname) {

	unsigned short flag;
	unsigned short c_inode;

	//open file system
	if ((fsfd = open("v6fs", O_RDWR)) < 0) {
		printf("ERROR in cd: fsfd<0\n");
		exit(1);
	}

	//reach the datablock
	unsigned short dblock = read_dblock_addr_dir(fsfd, cinode);
	lseek(fsfd, dblock, SEEK_SET);

	//search for the entry
	int entry_num = get_entry_num(fsfd, fname);

	if (entry_num < 32) {
		lseek(fsfd, (dblock + (entry_num * 16)), SEEK_SET);
		read(fsfd, &c_inode, 2);

		lseek(fsfd, ((BLOCK_SIZE * 2) + c_inode * 32), SEEK_SET);
		read(fsfd, &flag, 2);
		unsigned long x = flag % 2;
		unsigned long y = 1;
		flag = flag / 2;
		while (flag != 0) {
			x = (flag % 2) * y * 10 + x;
			flag = flag / 2;
			y = y * 10;
		}
		printf("Inode Flag value :: %lu\n", x);
	}else{
		printf("Chkflg : %s : No such file or directory\n", fname);
	}
	close(fsfd);
}

void saveffscontents(unsigned long arg1, unsigned long arg2)
{
	int initload;
	if ((initload = open("ffscontents", O_CREAT | O_RDWR | O_TRUNC, 0664)) < 0)
	{
		printf("ERROR in saveffscontents: fsfd<0\n");
		exit(1);
	}
	write(initload, &cinode, sizeof(cinode));
	write(initload, &cparentinode, sizeof(cinode));
	write(initload, &iptr, sizeof(iptr));
	write(initload, &arg1, sizeof(arg1));
	write(initload, &arg2, sizeof(arg2));
	close(initload);
}

void loadffs(unsigned long arg1, unsigned long arg2)
{
	int initload, fsfd;
	int i;
	if ((fsfd = open("v6fs", O_RDONLY)) < 0)
	{
		printf("ERROR in loadffs: fsfd<0\n");
		exit(1);
	}
	if ((initload = open("ffscontents", O_RDONLY)) < 0)
	{
		printf("ERROR in loadffs: fsfd<0\n");
		exit(1);
	}

	lseek(fsfd, BLOCK_SIZE, SEEK_SET);
	read(fsfd, &superblock, sizeof(struct superblock));
	read(initload, &cinode, sizeof(cinode));
	read(initload, &cparentinode, sizeof(cparentinode));
	read(initload, &iptr, sizeof(iptr));
	read(initload, &arg1, sizeof(arg1));
	read(initload, &arg2, sizeof(arg2));

	close(initload);
	close(fsfd);
}

int main() {
	iptr = 0;
	int rvalue;
	char buffer[20];
	char commandarg[50];
	char unxfile[14], v6file[14];
	unsigned long arg1 = 0, arg2 = 0;

	if ((fsfd = open("v6fs", O_RDONLY)) < 0)
		{

			printf("\nFile system not initialized\n");
			printf("\nPlease initialize the File System\n");
			scanf("%s", buffer);
			while (strcmp(buffer, "Init"))
			{
				printf("Please initialize the File System\n");
				scanf("%s", buffer);
			}
			scanf("%lu", &arg1);
			scanf("%lu", &arg2);
			rvalue = FileSystemInitialization(arg1, arg2);
			printf("$v6fs%s> ", currentpath);
		}
	else
		{
			cinode=0;cparentinode=0;
			strcpy(currentpath, "/");
			loadffs(arg1, arg2);
			printf("FFS loaded. Please enter the commands\n");
			printf("$v6fs%s> ", currentpath);
		}

	while (1) {
		scanf("%s", buffer);
		if (!strcmp(buffer, "Mkdir")) {
			scanf("%s", commandarg);
			if(strcmp(EMPTY_BLOCK, commandarg)){
				rvalue = Mkdir(arg2, commandarg);
			}else{
				printf("%s is reserved word and cannot be used for file names in this file system\n",commandarg);
			}
			printf("$v6fs%s> ", currentpath);
		} else if(!strcmp(buffer, "Cpin")){
			scanf("%s", unxfile);
			scanf("%s", v6file);
			if (strcmp(EMPTY_BLOCK, v6file)) {
				rvalue = Cpin(arg2, unxfile, v6file);
			} else {
				printf(
						"%s is reserved word and cannot be used for file names in this file system\n",
						v6file);
			}

			printf("$v6fs%s> ", currentpath);
		} else if (!strcmp(buffer, "Cd")) {
			scanf("%s", commandarg);
			if(!strcmp(commandarg, "EMPTY"))
				{
					printf("Cd: %s: No such file or directory\n", commandarg);
					printf("$v6fs%s> ", currentpath);
				}
			rvalue = Cd(commandarg);
			printf("$v6fs%s> ", currentpath);
		} else if (!strcmp(buffer, "Quit")) {
			saveffscontents(arg1, arg2);
			exit(1);
		} else if (!strcmp(buffer, "Pwd")) {
			printf("%s\n", currentpath);
			printf("$v6fs%s> ", currentpath);
		} else if (!strcmp(buffer, "Rmdir")) {
			scanf("%s", commandarg);
			if(strcmp(EMPTY_BLOCK, commandarg)){
				Rmdir(commandarg, arg2);
			}

			printf("$v6fs%s> ", currentpath);
		} else if (!strcmp(buffer, "Chmod")) {

			unsigned int arg1;

			scanf("%o", &arg1);
			scanf("%s", commandarg);

			Chmod((unsigned short) arg1, commandarg);
			//check_flag_values();
			printf("$v6fs%s> ", currentpath);
		}else if (!strcmp(buffer, "Rm")){
			scanf("%s", commandarg);
			if(strcmp(EMPTY_BLOCK, commandarg)){
				Rmfile(commandarg, arg2);
			}
			printf("$v6fs%s> ", currentpath);
		}else if(!strcmp(buffer, "Cpout")){
			scanf("%s", unxfile);
			scanf("%s", v6file);
			Cpout(unxfile, v6file);
			printf("$v6fs%s> ", currentpath);
		}else if(!strcmp(buffer, "Ls")){
			display_dir_contents(cinode);
			printf("$v6fs%s> ", currentpath);
		}else if(!strcmp(buffer, "Chkflg")){
			scanf("%s", commandarg);
			check_flag_values(commandarg);
			printf("$v6fs%s> ", currentpath);
		}

		else {
			printf("%s : command not found\n", buffer);
			printf("$v6fs%s> ", currentpath);
			//exit(1);
		}
	}
	return 0;
}

